﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml;
using System.Threading;
using System.Runtime.InteropServices;
using System.Media;
using System.Timers;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;

namespace eBrain
{
    public class Experiment
    {
        #region ATTRIBUTES
        //Parte per usare il beep di sistema (con frequenza e durata) 
        [DllImport("Kernel32.dll")]
        public static extern bool Beep(UInt32 frequency, UInt32 duration);

        protected Mutex UpdateCanvasMtx = new Mutex();

        public bool bStandAlone = false; //flag per identificare se sto eseguendo un test singolo o inserito in una batteira con TCW

        public enum _ExperimentType { Unknown, Saccade, MultipleChoice, VisualSearch, SmoothPursuit, CardGambling, Token, Wisconsin, AAC_VirtualKeyboard };

        //TODO: da aggiungere il test dello stato
        public enum ExperimentStatus { Init, BlackScreen, Calibration, DisplayTaskInstructions, DisplayTrainingResults, CountDown, DoingTrials, SaveResults, Ending, UpdatingGraphics, Error };
        public enum TrialStatus { Zero, PreRun, Execution, WaitingForAcknowledge, Timeout, PostRun, Error };
        public enum InputMode { MouseEmulation, Touch, Eyetracker, P300Speller };

        public Bitmap WorkingCanvas;
        //        public Bitmap myTempCanvas;

        public EyeTracker ET;

        public bool ExperimentStopped = false;

        public int ScreenWidth = 1024;
        public int ScreenHeight = 768;

        public int TriggerSide = 0; //0 = sinistra, 1 = destra
        public string sTrialOutcome;
        public int iTrialValue;
        public System.Timers.Timer DelayedStimulusTimer;

        public bool bROISelected = false;

        public string sDataPath;
        public string FileNameResults;
        public string sSubjectID;

        //public int prova_int { get; set; }
        public bool bUseSounds = false;
        public bool bDoCountdown = false;

        public TestControlWindow myTCW;
        public ExperimentMainForm myExpW;
        public TransparentLayer myTranspLayer;

        public ExperimentStatus exp_status;
        //public TrialStatus trial_status;
        public InputMode ExpInputMode;

        public string ExperimentName;
        public string sExperimentType;
        public _ExperimentType ExperimentType;

        //string sROIFolder;

        public DateTime ExperimentStartTime; //, TrialStartTime;

        public int iColorCounter = 0;

        public string sCountDown;

        //int NumOfTrials;
        public int Timeout;
        //int NumOfROIs;
        public Color bkgrnd, frgrnd, HighlightColor, HighlightSelectionColor;
        public bool bHighlightChoice, bHighlightSelection;
        public int HighlightSelectionTime;
        public bool bConstantRois, bConstantROIImages, bNextButton;
        public bool bAlphabetTypeROIs;
        public int BorderSize;
        public int WritingBarHeight;

        public int CumSumTiming; //sommacumulativa dei ritardi accumulati (per calcolare il tempo medio delle risposte) 
        public int NumOfTimeout;
        public int TrialScore;

        public int delta_x, last_x = -1000;

        //--------------------------------------------
        //Parte generica
        //--------------------------------------------
        public bool bWaitForSpaceKey = false;
        public bool bWaitForSelectionVerify = false;

        //Istruzioni
        public string sInstructions;
        public string sInstructionsHeader;
        public int InstructionsFontSize;
        public bool bUseInstructionsImages = false;

        XmlNodeList InstructionsImages;

        //Presentazione del risultato finale
        public bool bShowTrainingScore = false;
        public string CorrectScoreString, ErrorScoreString, TimeoutScoreString;

        public string sImagePath;

        //        public bool bDrawPointingHand = false;  //flag per abilitare il disegno della mano 
        public Point ActualGazePoint;

        //public Bitmap Mask, Target;
        public int UpdateTime, TargetSize;
        public string TargetShape, TargetImage;

        public bool bTrainingAvailable = false;
        public bool bTrainingMode = false;
        public int iHighlightSize, iHighlightSelectionSize;

        public XmlNodeList ROIs;
        public XmlNodeList Trials;
        public XmlNodeList TrainingTrials;
        public XmlNodeList ActROIs;  //lista delle ROI dell'attuale Trial (contengono i dati relativi ai valori ed alle altre indicazioni specifice del trial

        //parte vocale
        public bool bUseAudioCues = false;
        public bool bUseAudioInstructions = false;
        protected SoundPlayer SpeechPlayer = new System.Media.SoundPlayer();
        protected string sAudioPath, AudioInstructionsFilename;

        public RoiList myROIList;
        public List<ResultListElement> myResultList;

        public bool bHighlighted = false;

        public Font AlphabetFont;

        public int ActualTrialNumber = 0;

        public System.Timers.Timer CoordUpdateTimer = new System.Timers.Timer();
        public System.Timers.Timer TimeoutTimer = new System.Timers.Timer();
        //System.Timers.Timer CountDownTimer = new System.Timers.Timer();

        public bool bEyelinkConnected = false;
        public string EDFFilename = "ebrain-2.edf";
        public bool SkipETCalibration = false; 


        //public int TimerPeriod_ms;
        public bool bTimeoutElapsed = false;

        public bool EndOfTrials = false;


        public Logger myLog;

        //public const int NO_ROI = -1;
        public int ROI_Selected = RoiList.NO_ROI;
        //public int ROI_SelectedVerified = RoiList.NO_ROI;

        public SoundPlayer SPTimeout, SPSelection, SPGambleWin, SPGambleLose;

        //int roi_sel_size;
        public int ScanPeriodMs, SelectionTimeWindowMs, SelectionTimePerc;

        //--------------------------------------------------------------------
        //        Parte Per il timing
        //--------------------------------------------------------------------

        public List<DateTime> TrialStartTimeList;



        //--------------------------------------------------------------------
        //        Parte dei Socket UDP per il collegamento con BCI
        //--------------------------------------------------------------------
        byte[] databuffer = new byte[1024];

        public string PauseOn = "PauseApplication 1\n";
        public string PauseOff = "PauseApplication 0\n";
        public string sSpellerMenu = "SpellerMenu ";
        public string sGoto = "<GOTO ";
        public string sEnd = "Running 0\n";

        UdpClient UDPClientToConnIn;

        Socket SkFromConnOut = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        Socket SkFromDestAddr = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

        //public byte[] dataPauseOn, dataPauseOff, gotoNext, end;
        public List<DateTime> SpaceKeyPressTimeList;
        public List<string> BCISelectionList; //lista dei valori restituiti dalla macchina

        public int min_ROI_ID, max_ROI_ID;


        bool bWaitingForFirstData = true;
        //--------------------------------------------------------------------------    

        #endregion

        #region Constructor
        public Experiment(TestControlWindow _tcw, ExperimentMainForm _expw)//, int _w, int _h)
        {
            try
            {
                myTCW = _tcw;
                myExpW = _expw;

                //myExpW.Invalidate();
                //Thread.Sleep(100);

                Screen[] sc;
                sc = Screen.AllScreens;

                if (myExpW.bMultiMonitor)
                {
                    ScreenWidth = sc[1].WorkingArea.Width;
                    ScreenHeight = sc[1].WorkingArea.Height;
                }
                else
                {
                    ScreenWidth = sc[0].WorkingArea.Width;
                    ScreenHeight = sc[0].WorkingArea.Height;
                }

                myLog = myTCW.myLog;// new Logger("EL-Trials.log");

                SPTimeout = new SoundPlayer(Properties.Resources.timeout_future);
                SPSelection = new SoundPlayer(Properties.Resources.selection);
                SPGambleWin = new SoundPlayer(Properties.Resources.Applause);
                SPGambleLose = new SoundPlayer(Properties.Resources.ohh_noo);

                WorkingCanvas = new Bitmap(ScreenWidth, ScreenHeight);

                TrialStartTimeList = new List<DateTime>();
            }
            catch (Exception ex)
            {
                myLog.Log("ExperimentWindow error : " + ex.Message + " " + ex.Source);
                MessageBox.Show("ExperimentWindow error : " + ex.Message + " " + ex.Source);
            }
        }

        public Experiment(ExperimentMainForm _expw, string datapath, string logpath, string sInputMode)//, int _w, int _h)
        {
            try
            {
                myTCW = null;
                bStandAlone = true;

                //seleziono la modalità di input
                if (sInputMode.ToLower().Contains("eyetracking") || sInputMode.ToLower().Contains("eyetracker") || sInputMode.ToLower().Contains("et"))
                {
                    ExpInputMode = InputMode.Eyetracker;
                }
                else
                    if (sInputMode.ToLower().Contains("mouse"))
                    {
                        ExpInputMode = InputMode.MouseEmulation;
                    }
                    else
                        if (sInputMode.ToLower().Contains("bci") || sInputMode.ToLower().Contains("speller") || sInputMode.ToLower().Contains("p300"))
                        {
                            ExpInputMode = InputMode.P300Speller;
                        }
                        else
                            if (sInputMode.ToLower().Contains("touch"))
                            {
                                ExpInputMode = InputMode.Touch;
                            }

                myExpW = _expw;

                TrialStartTimeList = new List<DateTime>();

                //Se il sistema di input è lo speller, allora attivo la cattura del tasto Spazio per andare avanti con gli esperimenti
                //e creo la lista dei corrispondenti tempi di pressione del tasto 
                if (ExpInputMode == InputMode.P300Speller)
                {
                    SpaceKeyPressTimeList = new List<DateTime>();
                    BCISelectionList = new List<string>();

                    myExpW.KeyDown += new KeyEventHandler(OnKeyDown);

                    myExpW.P300SpellerMode = true;
                }

                //myExpW.Invalidate();
                //Thread.Sleep(100);

                Screen[] sc;
                sc = Screen.AllScreens;

                if (myExpW.bMultiMonitor)
                {
                    ScreenWidth = sc[1].WorkingArea.Width;
                    ScreenHeight = sc[1].WorkingArea.Height;
                }
                else
                {
                    ScreenWidth = sc[0].WorkingArea.Width;
                    ScreenHeight = sc[0].WorkingArea.Height;
                }


                myLog = new Logger("-.log", logpath);

                sDataPath = datapath + "\\" + DateTime.Now.Year.ToString("0000") + "-" + DateTime.Now.Month.ToString("00") + DateTime.Now.Day.ToString("00") + "\\";

                if (!Directory.Exists(sDataPath))
                    Directory.CreateDirectory(sDataPath);

                SPTimeout = new SoundPlayer(Properties.Resources.timeout_future);
                SPSelection = new SoundPlayer(Properties.Resources.selection);
                SPGambleWin = new SoundPlayer(Properties.Resources.Applause);
                SPGambleLose = new SoundPlayer(Properties.Resources.ohh_noo);

                WorkingCanvas = new Bitmap(ScreenWidth, ScreenHeight);

            }
            catch (Exception ex)
            {
                myLog.Log("ExperimentWindow error : " + ex.Message + " " + ex.Source);
                MessageBox.Show("ExperimentWindow error : " + ex.Message + " " + ex.Source);
            }
        }

        public void setTranspLayer(ref TransparentLayer mytl)
        {
            if (mytl != null)
                myTranspLayer = mytl;

            //Proviamo a vedere se cosi' migliorano le cose 
            //if (myExpW != null)
            //    mytl.Parent = myExpW;
        }

        public void StartUDPListening()
        {
            //dataPauseOn = Encoding.ASCII.GetBytes(PauseOn);
            //dataPauseOff = Encoding.ASCII.GetBytes(PauseOff);
            //end = Encoding.ASCII.GetBytes(sEnd);

            SkFromConnOut.Bind(new IPEndPoint(IPAddress.Any, 9050));
            SkFromDestAddr.Bind(new IPEndPoint(IPAddress.Any, 9052));

            UDPClientToConnIn = new UdpClient("127.0.0.1", 9051);

            EndPoint Remote = new IPEndPoint(IPAddress.Any, 0);
            SkFromConnOut.BeginReceiveFrom(databuffer, 0, databuffer.Length,
                SocketFlags.None, ref Remote, new AsyncCallback(OnReceiveFromConnectorOutput), SkFromConnOut);
        }

        private void OnReceiveFromConnectorOutput(IAsyncResult ar)
        {
            try
            {
                Socket recvSock = (Socket)ar.AsyncState;
                EndPoint epSender = new IPEndPoint(IPAddress.Any, 0);

                int msgLen = SkFromConnOut.EndReceiveFrom(ar, ref epSender);

                byte[] localMsg = new byte[msgLen];
                Array.Copy(databuffer, localMsg, msgLen);

                string outcome = Encoding.ASCII.GetString(localMsg, 0, msgLen);
                //textBox1.Text = outcome;

                if (bWaitingForFirstData)
                {
                    bWaitingForFirstData = false;

                    //mi prendo lo schermo dello speller
                    myExpW.Invoke(new EventHandler(delegate { myExpW.HijackP3SpellerGraphics(); }));

                    SendUDPString(PauseOn);
                    //UDPClientToConnIn.Send(dataPauseOn, dataPauseOn.Length);

                    StartTrials(false);

                    myExpW.Invoke(new EventHandler(delegate
                    {
                        myExpW.SetFrontWindow();
                        myExpW.HightlightBorder();
                        //WaitForSpaceToStart();
                    }));

                    bWaitForSpaceKey = true;
                    //TODO: vedere se devo mettere qualcosa qui.DoSomething();

                    //SendUDPString(PauseOff);
                    //UDPClientToConnIn.Send(dataPauseOff, dataPauseOff.Length);

                    EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
                    SkFromDestAddr.BeginReceiveFrom(databuffer, 0, databuffer.Length,
                        SocketFlags.None, ref newClientEP, new AsyncCallback(OnReceiveFromDestinationAddress), SkFromDestAddr);
                }
                else
                {
                    //Mi metto in attesa della ripartenza, cioè del messaggio "Paused 0". 
                    if (outcome.Contains("PauseApplication 0")) //(outcome.Contains("Paused 0") || outcome.Contains("PausedApplication 0") || 
                    {
                        // Ho ricevuto "Paused 0", mi metto ad attendere che lo speller trasmetta il risultato della selezione
                        EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
                        SkFromDestAddr.BeginReceiveFrom(databuffer, 0, databuffer.Length,
                            SocketFlags.None, ref newClientEP, new AsyncCallback(OnReceiveFromDestinationAddress), SkFromDestAddr);
                    }
                    else
                    {
                        //non ho ancora ricevuto "Paused 0", mi metto in ricezione di altri dati
                        EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
                        SkFromConnOut.BeginReceiveFrom(databuffer, 0, databuffer.Length,
                            SocketFlags.None, ref newClientEP, new AsyncCallback(OnReceiveFromConnectorOutput), SkFromConnOut);
                    }
                    //this.Invalidate();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + " " + ex.Source, "BCI2000_eBrainBridge - OnReceiveFromConnectorOutput",
               MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }


        private void OnReceiveFromDestinationAddress(IAsyncResult ar)
        {
            try
            {
                //metto in pausa lo Speller
                SendUDPString(PauseOn);

                Socket recvSock = (Socket)ar.AsyncState;
                EndPoint epSender = new IPEndPoint(IPAddress.Any, 0);
                string str_result;

                //UDPClientToConnIn.Send(dataPauseOn, dataPauseOn.Length);

                if (ExpInputMode == InputMode.P300Speller)
                {
                    int msgLen = recvSock.EndReceiveFrom(ar, ref epSender);
                    byte[] localMsg = new byte[msgLen];
                    Array.Copy(databuffer, localMsg, msgLen);

                    str_result = (Encoding.ASCII.GetString(localMsg, 0, msgLen)).TrimEnd('\n');
                    if (str_result.Contains(" "))
                    {
                        String[] sa = str_result.Split(' ');

                        if (sa.Length > 0)
                        {
                            str_result = sa[1];
                        }
                        else
                        {
                            str_result = str_result.Trim();
                        }
                    }

                    //TODO: verificare che cosi' si prenda sempre la carta corrispondente a quella selezionata
                    if ("abcdefghilmnopqrstuvz".Contains(str_result.ToLower()))
                        ROI_Selected = "abcdefghilmnopqrstuvz".IndexOf(str_result.ToLower());
                    else
                        ROI_Selected = int.Parse(str_result);

                    //Aggiungo l'ultimo dato alla lista dei risultati dello speller

                    //TODO: controllare la seguete linea
                    //if (ExperimentType == _ExperimentType.Wisconsin)
                    //    BCISelectionList.Add(ROI_Selected.ToString());
                    //else
                    BCISelectionList.Add(myROIList.myRoiList[ROI_Selected].sItem);
                    //myLog.Log("selected: " + myROIList.myRoiList[ROI_Selected].sItem.ToString());

                    //Gestisco la selezione
                    SelectionHandler();

                    SetKeyDownControlToTransparentLayer();

                    //

                    //                    Application.DoEvents();
                    //                    Thread.Sleep(100);

                    //SetKeyDownControlToExpMainForm();

                    //WaitForSpaceToStart();
                }


                //if (!EndOfTrials)
                //{
                //    //TODO: completare nel caso si abbia a che fare con altri input

                //    //Rimuovo dalla pausa lo speller
                //    //SendUDPString(PauseOff);
                //    //UDPClientToConnIn.Send(dataPauseOff, dataPauseOff.Length);

                //    EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
                //    SkFromConnOut.BeginReceiveFrom(databuffer, 0, databuffer.Length,
                //        SocketFlags.None, ref newClientEP, new AsyncCallback(OnReceiveFromConnectorOutput), SkFromConnOut);
                //}
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + " " + ex.Source + "\n" + ex.StackTrace, "OnReceiveFromDestinationAddress",
             MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        public void SetKeyDownControlToTransparentLayer()
        {
            myExpW.KeyDown -= OnKeyDown;
            //myTranspLayer.KeyDown += new KeyEventHandler(myTranspLayer.OnKeyDown);
            myTranspLayer.KeyDown += new KeyEventHandler(OnKeyDown);

            bWaitForSpaceKey = false;
            bWaitForSelectionVerify = true;
        }

        public void SetKeyDownControlToExpMainForm()
        {
            myTranspLayer.KeyDown -= OnKeyDown;
            myExpW.KeyDown += new KeyEventHandler(OnKeyDown);

        }


        public void SendUDPString(String s_tosend)
        {
            byte[] ba;
            if (s_tosend != "")
            {
                try
                {
                    ba = Encoding.ASCII.GetBytes(s_tosend);

                    UDPClientToConnIn.Send(ba, ba.Length);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("SendUDPString error: " + ex.Message);
                }
            }
        }

        public void OnKeyDown(object sender, KeyEventArgs e)
        {
            if (bWaitForSelectionVerify)
            {
                switch (e.KeyCode)
                {
                    case Keys.Left:
                        if (--ROI_Selected < min_ROI_ID)
                            ROI_Selected = max_ROI_ID;

                        myExpW.Invoke(new EventHandler(delegate { myTranspLayer.UpdateSelectedArea(ROI_Selected); }));
                        break;

                    case Keys.Right:
                        if (++ROI_Selected > max_ROI_ID)
                            ROI_Selected = min_ROI_ID;
                        myExpW.Invoke(new EventHandler(delegate { myTranspLayer.UpdateSelectedArea(ROI_Selected); }));


                        break;

                    case Keys.Enter:

                        bWaitForSelectionVerify = false;

                        if (ExpInputMode == InputMode.P300Speller)
                        {
                            myExpW.Invoke(new EventHandler(delegate { myTranspLayer.SendToBack(); }));
                            //TurnOffTransparentLayer();
                            myExpW.Invoke(new EventHandler(delegate { myExpW.SetFrontWindow(); }));

                            Application.DoEvents();
                            SetKeyDownControlToExpMainForm();
                        }

                        PerformSelection();

                        Thread.Sleep(100);

                        //tentativo
                        myExpW.Invoke(new EventHandler(delegate
                    {
                        // myExpW.SetFrontWindow();

                        myExpW.HightlightBorder();
                        //WaitForSpaceToStart();
                    }));

                        bWaitForSpaceKey = true;
                        //choice_performed = true;

                        break;
                }
            }
            else
                if (bWaitForSpaceKey && e.KeyCode == Keys.Space)
                {
                    //aggiungo il tempo in cui è stato premuto il pulsante di spazio
                    SpaceKeyPressTimeList.Add(DateTime.Now);

                    bWaitForSpaceKey = false;
                    myExpW.Invoke(new EventHandler(delegate { myExpW.ClearHighlightBorder(); myExpW.Refresh(); }));

                    if (!EndOfTrials)
                    {
                        //TODO: completare nel caso si abbia a che fare con altri input

                        //Rimuovo dalla pausa lo speller
                        SendUDPString(PauseOff);
                        //UDPClientToConnIn.Send(dataPauseOff, dataPauseOff.Length);

                        EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
                        SkFromConnOut.BeginReceiveFrom(databuffer, 0, databuffer.Length,
                            SocketFlags.None, ref newClientEP, new AsyncCallback(OnReceiveFromConnectorOutput), SkFromConnOut);
                    }

                    //StartP300Sequence();
                    //textBlock1.Text = "You Entered: " + textBox1.Text;
                }
        }

        //public void WaitForSpaceToStart()
        //{

        //    myExpW.Invoke(new EventHandler(delegate
        //    {
        //        myExpW.HightlightBorder();
        //        myExpW.BringToFront();
        //        myExpW.Activate();
        //        myExpW.Refresh();
        //    }));

        //}


        public void TurnOffTransparentLayer()
        {
            myTranspLayer.Invoke(new EventHandler(delegate
            {
                myTranspLayer.TopMost = false;
                myTranspLayer.WindowState = FormWindowState.Normal;
                //myTranspLayer.Visible = false;
                myTranspLayer.SendToBack();

            }));
        }

        #endregion

        #region VIRTUAL METHODS

        virtual public void CustomInit()
        {
        }

        virtual public void CustomXMLLoad(XmlDocument xmldoc)
        {
        }

        virtual public void UpdateCanvas()
        {
        }

        virtual public void SelectionHandler()
        {
        }

        virtual public void PerformSelection()
        {
        }

        virtual public void PlayCue()
        {
            string audiofile;

            if (bTrainingMode)
                audiofile = TrainingTrials.Item(ActualTrialNumber).SelectSingleNode("AudioCue").InnerText;
            else
                audiofile = Trials.Item(ActualTrialNumber).SelectSingleNode("AudioCue").InnerText;

            audiofile = sAudioPath + audiofile;

            SpeechPlayer.SoundLocation = audiofile;
            SpeechPlayer.PlaySync();
        }


        public virtual void Custom_DoNextTrial()
        {
            UpdateCanvas();

            myExpW.Invalidate();

            //Thread.Sleep(100);
            if (!bStandAlone)
                PaintCanvasOnTCW();
        }

        /// <summary>
        /// Fa partire il timer periodico di aggiornamento delle coordinate schermo lette (dal mouse o dall'Eyelink)
        /// </summary>
        virtual public void StartPeriodicTimer()
        {
            CoordUpdateTimer.Elapsed += new System.Timers.ElapsedEventHandler(UpdateCoordTimerElapsedEvent);
            CoordUpdateTimer.Interval = ScanPeriodMs;
            CoordUpdateTimer.Start();

            //myLog.Log("Timer Started");
        }

        virtual public void StopPeriodicTimer()
        {
            CoordUpdateTimer.Stop();
            CoordUpdateTimer.Elapsed -= UpdateCoordTimerElapsedEvent;

            //myLog.Log("Timer Stopped");
        }


        virtual public void TimeoutTimer_Elapsed(object obj, EventArgs e)
        {

            myResultList.Add(new ResultListElement(0, "T", "Timeout"));

            NumOfTimeout++;

            TimeoutTimer.Stop();
            StopPeriodicTimer();

            //NumOfLetters++;

            myLog.Log("Trial " + ActualTrialNumber.ToString() + " Timout : ");

            if (bUseSounds)
            {
                //SoundPlayer sp = new SoundPlayer(Properties.Resources.timeout_future);
                SPTimeout.Play();
            }

            //<DataViewer>
            if (bEyelinkConnected)
                ET.SendCommand("TRIAL_RESULT 0");

            if (bTrainingMode)
            {
                if (++ActualTrialNumber >= TrainingTrials.Count)
                    FromTrainingToTrials();
                else
                {
                    DoNextTrial();
                }
            }
            else
            {
                if (++ActualTrialNumber >= Trials.Count)
                {
                    ActualTrialNumber = Trials.Count - 1;
                    EndOfTrials = true;

                    //SaveResults();
                    EndOfTest();
                }
                else
                {
                    DoNextTrial();
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        virtual public void UpdateCoordTimerElapsedEvent(object sender, EventArgs e)
        {
            float x = 0, y = 0;
            int act_x = 0, act_y = 0;

            switch (ExpInputMode)
            {

                case InputMode.MouseEmulation:

                    //Sto usando il mouse in emulazione dell'ET

                    act_x = System.Windows.Forms.Cursor.Position.X;
                    act_y = System.Windows.Forms.Cursor.Position.Y;

                    if (myExpW.bMultiMonitor)
                        act_x -= myExpW.offset;

                    x = act_x;
                    y = act_y;
                    break;

                case InputMode.Eyetracker:
                    try
                    {
                        ET.UpdateGaze(ref act_x, ref act_y);
                    }
                    catch (Exception ex)
                    {
                        myLog.Log("MyTimerElapsedEvent Error: " + ex.Message.ToString() + "\r\n" + ex.StackTrace);
                        MessageBox.Show("MyTimerElapsedEvent Error: " + ex.Message.ToString() + "\r\n" + ex.StackTrace);
                    }

                    break;

                case InputMode.Touch:
                    break;
            }

            //Aggiorno il punto di mira (per disegnare il puntatore)
            ActualGazePoint.X = act_x;
            ActualGazePoint.Y = act_y;

            //lock (RoiListLock)
            //RoiMutex.WaitOne();
            lock (myROIList)
                ROI_Selected = myROIList.IsSustainedInROI(act_x, act_y);
            //RoiMutex.ReleaseMutex();
            //----------------------------------------------------------
            //             E' stato selezionata un'area
            //----------------------------------------------------------

            //controllo se sia stato selezionato qualche tasto
            if (!bROISelected && ROI_Selected >= 0)
            {
                bROISelected = true;
                TimeoutTimer.Stop();
                StopPeriodicTimer();
                SelectionHandler();

                //Proviamo a vedere se cosi' funziona
            }
            else
            {
                //lock (RoiListLock)
                //{
                //RoiMutex.WaitOne();
                lock (myROIList)
                {
                    if (myROIList.ROI_to_highlight != RoiList.NO_ROI)
                    {
                        bHighlighted = true;
                        //int i = myROIList.ROI_to_highlight;

                        //lo segnalo all'oggetto form, con una persistenza di 500 ms (TODO: verificare che sia adatta)
                        //myExpW.UpdateSelectedArea(myROIList.myRoiList[i].x0, myROIList.myRoiList[i].y0,
                        //    myROIList.myRoiList[i].width, myROIList.myRoiList[i].height,
                        //    HighlightColor, iHighlightSize, 0);
                        myExpW.UpdateInROIArea(myROIList.GD_AreaCovered.x, myROIList.GD_AreaCovered.y,
                            myROIList.GD_AreaCovered.w, myROIList.GD_AreaCovered.h,
                            HighlightColor, iHighlightSize, 0);

                        //HighlightSelection();
                        myExpW.Invalidate();
                    }
                    else
                    {
                        if (bHighlighted)
                        {
                            myExpW.TurnOffInROIArea();
                            myExpW.Invalidate();
                            bHighlighted = false;
                        }
                    }
                }
            }
        }

        #endregion


        public void Pause()
        {
            if (CoordUpdateTimer.Enabled)
                CoordUpdateTimer.Enabled = false;

            if (TimeoutTimer.Enabled)
                TimeoutTimer.Enabled = false;
        }

        public void UnPause()
        {
            if (!CoordUpdateTimer.Enabled)
                CoordUpdateTimer.Enabled = true;

            if (!TimeoutTimer.Enabled)
                TimeoutTimer.Enabled = true;
        }




        public void SetInputMode(InputMode _im)
        {
            ExpInputMode = _im;
        }

        public void StartExperiment(String ExpFileName, bool _do_sound, bool _do_countdown)
        {
            bool bTCWtopmost, bTCWtoplevel, bEXPWtopmost, bEXPWtoplevel;
            bool bDoTheRightThing = true;

            try
            {
                //myLog.Log("StartExperiment: begin");

                //Inizializzazione dell'esperimento
                exp_status = ExperimentStatus.Init;
                //trial_status = TrialStatus.Zero;

                bUseSounds = _do_sound;

                bDoCountdown = _do_countdown;
                //MainScreenGraphics = this.CreateGraphics();

                if (myResultList == null)
                    myResultList = new List<ResultListElement>();
                else
                    myResultList.Clear();

                CustomInit();

                //Carico il file dell'esperimento
                LoadXML(ExpFileName);

                //TimerPeriod_ms = _timer_period_ms;

                //facico partire il conteggio del tempo totale dell'esperimento
                ExperimentStartTime = DateTime.Now;

                if (ExpInputMode == InputMode.Eyetracker)
                {
                    ET = myTCW.myET;

                    bTCWtoplevel = myTCW.TopLevel;
                    bTCWtopmost = myTCW.TopMost;

                    bEXPWtoplevel = myExpW.TopLevel;
                    bEXPWtopmost = myExpW.TopMost;

                    //if (true)
                    //{
                    //ET.Init();

                    ET.ConnectEyelink(EDFFilename, ScreenWidth, ScreenHeight, ExperimentType == _ExperimentType.Saccade, ActualTrialNumber);

                    //Thread.Sleep(100);
                    if (bDoTheRightThing)
                    {
                        myExpW.calibrating = true;
                        myExpW.TopMost = true;
                        myExpW.TopLevel = true;
                    }

                    if( !SkipETCalibration)
                    ET.CalibrateEyelink(myExpW.myHandle, bkgrnd, frgrnd, ScreenWidth, ScreenHeight);

                    myExpW.calibrating = false;

                    //TODO: verificare se va bene
                    if (bDoTheRightThing)
                    {
                        myExpW.TopMost = false;
                    }//myExpW.TopLevel = false;

                }

                //Se c'è una fase di training, imposto il flag di conseguenza
                if( TrainingTrials != null)
                if (TrainingTrials.Count > 0)
                    bTrainingAvailable = true;

                //Imposto il Timer per il Timeout
                //TimeoutTimer.AutoReset = false; //il timer scatta una sola volta
                TimeoutTimer.Interval = Timeout * 1000;
                //if (ExperimentType == _ExperimentType.Saccade)
                //    TimeoutTimer.Elapsed += new System.Timers.ElapsedEventHandler(SaccadeTimeoutTimer_Elapsed);
                //else
                //    TimeoutTimer.Elapsed += new System.Timers.ElapsedEventHandler(TimeoutTimer_Elapsed);

                TimeoutTimer.Elapsed += new System.Timers.ElapsedEventHandler(TimeoutTimer_Elapsed);

                GC.Collect();
                GC.WaitForPendingFinalizers();

                ShowInitialInstructions();
                myExpW.Invoke(new EventHandler(delegate { myExpW.Refresh(); }));

                //Thread.Sleep(100);
                if (!bStandAlone)
                    PaintCanvasOnTCW();
                //myLog.Log("StartExperiment: end");

                if (!bStandAlone)
                    myTCW.Invoke(new EventHandler(delegate { myTCW.PromptToStartTrials(bTrainingAvailable); }));

                //TODO: da sistemare il meccanismo di 
                //if (ExpInputMode == InputMode.Eyetracker)
                //    myTCW.btRunTrials.Visible = true;
                //else
                //    bWaitForSpaceKey = true;
            }
            catch (Exception ex)
            {
                myLog.Log("StartExperiment Error: " + ex.Message.ToString() + " - " + ex.Source.ToString());
                MessageBox.Show("StartExperiment Error: " + ex.Message.ToString() + " - " + ex.Source.ToString());
            }
        }


        public void EndOfExperiment()
        {
            if (ExpInputMode == InputMode.Eyetracker)
            {
                ET.CloseEyetrackerConnection();
            }
        }

        virtual public void Custom_StartTrials()
        {
        }

        virtual public void StartTrials(bool TrainingMode)
        {
            bool go_on = false;
            try
            {
                //myLog.Log("Start Trials: begin");

                //azzero la somma cumulativa dei ritardi (TODO: da rivedere insieme alla struttura dei risultati resultlist) 
                CumSumTiming = 0;

                //azzero il numero di timeout
                NumOfTimeout = 0;

                //Azzero il punteggio dei Trial
                TrialScore = 0;

                bTrainingMode = TrainingMode;

                if (!bStandAlone)
                {
                    if (bTrainingMode)
                        myTCW.updateTrialsNumbers(0, TrainingTrials.Count);
                    else
                        myTCW.updateTrialsNumbers(0, Trials.Count);
                }

                Custom_StartTrials();

                ExperimentStopped = false;

                if (bDoCountdown)
                {
                    CountDown(3, 800);
                    //StartPeriodicTimer();

                    if (bUseSounds)
                        Beep(2000, 500);
                }

                UpdateCanvas();
                //if (bUseAudioCues)
                //    PlayCue(); 

                //cancello lo schermo e le ultime informazioni circa le parti da evidenziare
                myExpW.ClearScreen(bkgrnd);

                myExpW.HighlightEnabled = true;
                myExpW.Invalidate();
                //UpdateGraphics();
                //Thread.Sleep(100);
                if (!bStandAlone)
                    PaintCanvasOnTCW();

                //StartPeriodicTimer();

                EndOfTrials = false;

                if (ExpInputMode == InputMode.Eyetracker)
                {
                    //Aggiorno le ROI sullo schermo 
                    UpdateHostROI();
                    ET.StartRecording();
                }

                myExpW.SetSmoothPursuitMode((ExperimentType == _ExperimentType.SmoothPursuit));

                //myLog.Log("Start Trials: end");

                go_on = true;
            }
            catch (Exception ex)
            {
                myLog.Log("StartTrials error: " + ex.Message.ToString() + " - " + ex.Source.ToString());
                MessageBox.Show("StartTrials error: " + ex.Message.ToString() + " - " + ex.Source.ToString());
                go_on = false;
            }
            finally
            {
                //Se posso procedere, passo al primo Trial
                if (go_on && exp_status != ExperimentStatus.Ending)
                    DoNextTrial();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        virtual public void DoNextTrial()
        {
            try
            {
                myLog.Log("DoNextTrial: trial = " + ActualTrialNumber.ToString());

                //Console.WriteLine("DoNextTrial - 1");
                if (!bStandAlone)
                {
                    if (bTrainingMode)
                        myExpW.Invoke(new EventHandler(delegate { myTCW.updateTrialsNumbers(ActualTrialNumber + 1, TrainingTrials.Count); }));
                    else
                        myExpW.Invoke(new EventHandler(delegate { myTCW.updateTrialsNumbers(ActualTrialNumber + 1, Trials.Count); }));
                }
                Custom_DoNextTrial();

                //Aggiorno il dato relativo ai risultati
                if (!bStandAlone)
                    myExpW.Invoke(new EventHandler(delegate { myTCW.updateTrialFigures(TrialScore, NumOfTimeout); }));


                //Nel caso stia lavorando con l'ET, allora invio le opportune segnalazioni all'ET
                if (ExpInputMode == InputMode.Eyetracker && bEyelinkConnected)
                {
                    if (!bConstantRois)
                        UpdateHostROI();

                    ET_LabelTrial();
                }

                exp_status = ExperimentStatus.DoingTrials;

                GC.Collect();
                GC.WaitForPendingFinalizers();

                //Console.WriteLine("DoNextTrial - 2");
                //UpdateGraphics();

                if (ExperimentType != _ExperimentType.SmoothPursuit)
                {
                    myExpW.Invoke(new EventHandler(delegate { myExpW.Refresh(); }));
                }

                //TODO: verificare che vada bene qui 
                if (bUseAudioCues)
                    PlayCue();

                //Se sto lavorando con le BCI non faccio partire i timer (TODO: da verificare)
                if (ExpInputMode != InputMode.P300Speller)
                {

                    if (ExperimentType != _ExperimentType.SmoothPursuit)
                    {
                        //myExpW.Invoke(new EventHandler(delegate { myExpW.Refresh(); }));
                        TimeoutTimer.Start();
                    }

                    //Console.WriteLine("DoNextTrial - 3");
                    //myLog.Log("DoNextTrial: end");

                    StartPeriodicTimer();
                }

                TrialStartTimeList.Add(DateTime.Now);

                //TrialStartTime = DateTime.Now;

            }

            catch (Exception ex)
            {
                myLog.Log("DoNextTrial error: " + ex.Message.ToString() + " - " + ex.StackTrace);
                MessageBox.Show("DoNextTrial error: " + ex.Message.ToString() + " - " + ex.StackTrace);
            }
        }

        public void ShowInitialInstructions()
        {
            try
            {
                //myLog.Log("ShowInitialInstructions: begin");

                int x, y, w, h;
                string s;

                exp_status = ExperimentStatus.DisplayTaskInstructions;

                myExpW.LockCanvasUserCopy();
                myExpW.HighlightEnabled = false;
                Graphics g = Graphics.FromImage(myExpW.CanvasUserCopy);

                x = ScreenWidth / 10;
                y = ScreenHeight / 10;
                w = (ScreenWidth * 8) / 10;
                h = (ScreenHeight * 8) / 10;

                // Create font and brush.
                Font drawFont;
                if (InstructionsFontSize > 0)
                    drawFont = new Font("Arial", InstructionsFontSize);
                else
                    drawFont = new Font("Arial", 22);

                SolidBrush drawBrush = new SolidBrush(frgrnd);

                g.Clear(bkgrnd);
                g.DrawRectangle(new Pen(frgrnd), x, y, w, h);

                // Set format of string.
                StringFormat mydrawFormat = new StringFormat();
                mydrawFormat.Alignment = StringAlignment.Center;

                if (bTrainingMode)
                    s = sInstructionsHeader + "   (Training) \r\n\r\n" + sInstructions;
                else
                    s = sInstructionsHeader + "\r\n\r\n" + sInstructions;

                g.DrawString(s, drawFont, drawBrush, new Rectangle(x, y, w, h), mydrawFormat);

                //se ci sono da visualizzare delle immagini, lo faccio qui

                if (bUseInstructionsImages)
                {
                    //seleziono i nodi contenenti le immagini

                    foreach (XmlElement el in InstructionsImages)
                    {
                        string imgfile = el.SelectSingleNode("filename").InnerText.ToString();
                        int width = int.Parse(el.SelectSingleNode("w").InnerText.ToString());
                        int height = int.Parse(el.SelectSingleNode("h").InnerText.ToString());

                        string sx = el.SelectSingleNode("x").InnerText.ToString();
                        string sy = el.SelectSingleNode("y").InnerText.ToString();

                        int posX = int.Parse(sx.Replace(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol, ""));
                        int posY = int.Parse(sy.Replace(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol, ""));

                        posX = (posX * ScreenWidth) / 100 - width / 2;
                        posY = (posY * ScreenHeight) / 100 - height / 2;

                        Image myImag = Image.FromFile(sImagePath + imgfile);

                        g.DrawImage(myImag, posX, posY, width, height);
                    }
                }



                g.Dispose();

                //GraphMutex.ReleaseMutex();

                myExpW.CopyCanvas();

                myExpW.UnlockCanvasUserCopy();

                //myExpW.UpdateCanvas(myCanvas);
                //}
                //UpdateGraphics();
                //myExpW.Invalidate();


                //Parte vocale
                if (bUseAudioInstructions)
                {
                    SpeechPlayer.SoundLocation = sAudioPath + AudioInstructionsFilename;
                    SpeechPlayer.Play();
                }


                //myLog.Log("ShowInitialInstructions: end");
            }
            catch (Exception ex)
            {
                myLog.Log("ShowInitialInstructions error : " + ex.Message + " " + ex.Source);
                MessageBox.Show("ShowInitialInstructions error : " + ex.Message + " " + ex.Source);
            }
            //            AwakeWait(iInitialInstructionsTimeout * 1000);
            //Thread.Sleep(100);
        }

        /// <summary>
        /// Carica il file XML contenente il particolare esperimento.
        /// </summary>
        /// <param name="filename"></param>
        public void LoadXML(string filename)
        {
            try
            {
                myLog.Log("LoadXML: begin");

                XmlDocument xmldoc = new XmlDocument();
                xmldoc.Load(filename);

                //XmlNode xmln =;
                ExperimentName = xmldoc.SelectSingleNode("Experiment/Name").InnerText.ToString();
                sExperimentType = xmldoc.SelectSingleNode("Experiment/Type").InnerText.ToString();

                switch (sExperimentType)
                {
                    case "Saccade":
                        ExperimentType = _ExperimentType.Saccade;
                        break;
                    case "MultipleChoice":
                        ExperimentType = _ExperimentType.MultipleChoice;
                        break;
                    case "VisualSearch":
                        ExperimentType = _ExperimentType.VisualSearch;
                        break;
                    case "SmoothPursuit":
                        ExperimentType = _ExperimentType.SmoothPursuit;
                        break;
                    case "CardGambling":
                        ExperimentType = _ExperimentType.CardGambling;
                        break;
                    case "Token":
                        ExperimentType = _ExperimentType.Token;
                        break;
                    case "Wisconsin":
                        ExperimentType = _ExperimentType.Wisconsin;
                        break;
                    default:
                        ExperimentType = _ExperimentType.Unknown;
                        break;
                }

                ScanPeriodMs = int.Parse(xmldoc.SelectSingleNode("Experiment/ScanPeriod").InnerText.ToString());
                SelectionTimeWindowMs = int.Parse(xmldoc.SelectSingleNode("Experiment/SelectionTimeWindow").InnerText.ToString());
                SelectionTimePerc = int.Parse(xmldoc.SelectSingleNode("Experiment/SelectionTimePerc").InnerText.ToString());

                if (myROIList == null)
                    myROIList = new RoiList(SelectionTimeWindowMs / ScanPeriodMs, SelectionTimePerc);
                else
                {
                    myROIList.Clear();
                    ROI_Selected = RoiList.NO_ROI;
                    myROIList.UpdateParameters(SelectionTimeWindowMs / ScanPeriodMs, SelectionTimePerc);
                }

                //NumOfTrials = int.Parse(xmldoc.SelectSingleNode("Experiment/NumOfTrials").InnerText.ToString());
                Timeout = int.Parse(xmldoc.SelectSingleNode("Experiment/TrialTimeout").InnerText.ToString());
                //NumOfROIs = int.Parse(xmldoc.SelectSingleNode("Experiment/NumOfROIs").InnerText.ToString());
                bkgrnd = Color.FromName(xmldoc.SelectSingleNode("Experiment/Background").InnerText.ToString());
                myExpW.BackColor = bkgrnd;

                frgrnd = Color.FromName(xmldoc.SelectSingleNode("Experiment/Foreground").InnerText.ToString());
                bHighlightChoice = bool.Parse(xmldoc.SelectSingleNode("Experiment/HighlightChoice").InnerText.ToString());
                HighlightColor = Color.FromName(xmldoc.SelectSingleNode("Experiment/HighlightColor").InnerText.ToString());
                iHighlightSize = int.Parse(xmldoc.SelectSingleNode("Experiment/HighlightSizePx").InnerText.ToString());

                bHighlightSelection = bool.Parse(xmldoc.SelectSingleNode("Experiment/HighlightSelection").InnerText.ToString());
                HighlightSelectionColor = Color.FromName(xmldoc.SelectSingleNode("Experiment/HighlightSelectionColor").InnerText.ToString());
                iHighlightSelectionSize = int.Parse(xmldoc.SelectSingleNode("Experiment/HighlightSelectionSizePx").InnerText.ToString());

                HighlightSelectionTime = int.Parse(xmldoc.SelectSingleNode("Experiment/HighlightSelectionTime").InnerText.ToString());
                bConstantRois = bool.Parse(xmldoc.SelectSingleNode("Experiment/ConstantROIs").InnerText.ToString());
                bConstantROIImages = bool.Parse(xmldoc.SelectSingleNode("Experiment/ConstantROIImages").InnerText.ToString());

                sInstructionsHeader = xmldoc.SelectSingleNode("Experiment/InstructionsHeader").InnerText;
                sInstructions = xmldoc.SelectSingleNode("Experiment/InstructionsText").InnerText;
                InstructionsFontSize = int.Parse(xmldoc.SelectSingleNode("Experiment/InstructionsFontSize").InnerText);

                bUseInstructionsImages = bool.Parse(xmldoc.SelectSingleNode("Experiment/UseInstructionsImages").InnerText.ToString());

                if (bUseInstructionsImages)
                {
                    InstructionsImages = xmldoc.SelectNodes("Experiment/InstructionsImages/Image");
                }

                bShowTrainingScore = bool.Parse(xmldoc.SelectSingleNode("Experiment/ShowTrainingScore").InnerText.ToString());
                if (bShowTrainingScore)
                {
                    CorrectScoreString = xmldoc.SelectSingleNode("Experiment/CorrectScoreText").InnerText.ToString();
                    ErrorScoreString = xmldoc.SelectSingleNode("Experiment/ErrorScoreText").InnerText.ToString();
                    TimeoutScoreString = xmldoc.SelectSingleNode("Experiment/TimeoutScoreText").InnerText.ToString();

                }
                //Audio 
                sAudioPath = xmldoc.SelectSingleNode("Experiment/AudioPath").InnerText;
                bUseAudioInstructions = bool.Parse(xmldoc.SelectSingleNode("Experiment/UseAudioInstructions").InnerText.ToString());

                if (bUseAudioInstructions)
                    AudioInstructionsFilename = xmldoc.SelectSingleNode("Experiment/AudioInstructionFilename").InnerText;

                bUseAudioCues = bool.Parse(xmldoc.SelectSingleNode("Experiment/UseAudioCues").InnerText.ToString());

                sImagePath = xmldoc.SelectSingleNode("Experiment/ImagePath").InnerText;

                bNextButton = bool.Parse(xmldoc.SelectSingleNode("Experiment/NextTrialButton").InnerText.ToString());

                SkipETCalibration = bool.Parse(xmldoc.SelectSingleNode("Experiment/SkipETCalibration").InnerText);


                //TODO: verificare se quello che segue è da tenere o da cancellare
                if (ExperimentType == _ExperimentType.AAC_VirtualKeyboard)
                {
                    bAlphabetTypeROIs = true;
                    AlphabetFont = new Font(xmldoc.SelectSingleNode("Experiment/Alphabet/FontName").InnerText.ToString(),
                        int.Parse(xmldoc.SelectSingleNode("Experiment/Alphabet/FontSize").InnerText.ToString()));

                    BorderSize = int.Parse(xmldoc.SelectSingleNode("Experiment/Alphabet/BorderSize").InnerText.ToString());
                    WritingBarHeight = int.Parse(xmldoc.SelectSingleNode("Experiment/Alphabet/WritingBarHeight").InnerText.ToString());
                }
                else
                {
                    bAlphabetTypeROIs = false;
                    ROIs = xmldoc.SelectNodes("Experiment/ROIs/ROI");
                    //Se è un esperimento Gambling, allora non carico i trial (che non ci sono)
                    if (ExperimentType != _ExperimentType.CardGambling)
                    {
                        Trials = xmldoc.SelectNodes("Experiment/Trials/Trial");
                        TrainingTrials = xmldoc.SelectNodes("Experiment/Training/Trial");
                    }
                     
                    ActualTrialNumber = 0;
                }

                CustomXMLLoad(xmldoc);

                FileNameResults = ExperimentName + "-" + sSubjectID + "-" + myLog.Prefix;
                //myLog.Log("LoadXML: end");

            }
            catch (Exception ex)
            {
                myLog.Log("LoadXML error : " + ex.Message + " " + ex.Source.ToString());
                MessageBox.Show("LoadXML error : " + ex.Message + " " + ex.Source.ToString());
            }

        }


        public void StopExperiment()
        {
            //TODO: verificare che altre istruzioni è necessario inserire in questa routine di interruzione 

            ClearCanvas();
            //Thread.Sleep(100);

            //UpdateGraphics();
            myExpW.Refresh();

            exp_status = ExperimentStatus.SaveResults;

            //FErmo l'acquisizione senza la necessità di acquisire la registrazione
            if (ExpInputMode == InputMode.Eyetracker)
                ET.StopRecording(false, "");

            if (ExperimentType != _ExperimentType.SmoothPursuit)
                SaveCSVFile(sDataPath + "\\" + FileNameResults);

            exp_status = ExperimentStatus.Ending;

            TimeoutTimer.Stop();
            StopPeriodicTimer();
            TimeoutTimer.Elapsed -= TimeoutTimer_Elapsed;

            CoordUpdateTimer.Stop();
            CoordUpdateTimer.Elapsed -= UpdateCoordTimerElapsedEvent;
            myLog.Log("Timer Stopped");

            if (!bStandAlone)
                myTCW.ExperimentStopped();
            //myTCW.ExperimentCompleted();
            //this.Invoke(new EventHandler(delegate { myTCW.ExperimentCompleted(); }));
        }


        public void EndOfTest()
        {
            myExpW.HighlightEnabled = false;
            ClearCanvas();
            //Thread.Sleep(100);

            if (!bStandAlone)
                //effettuo l'ultimo aggiornamento sul punteggio
                myExpW.Invoke(new EventHandler(delegate { myTCW.updateTrialFigures(TrialScore, NumOfTimeout); }));

            //UpdateGraphics();
            myExpW.Invoke(new EventHandler(delegate { myExpW.Refresh(); }));

            exp_status = ExperimentStatus.SaveResults;

            if (ExpInputMode == InputMode.Eyetracker)
                ET.StopRecording(true, sDataPath + "\\" + FileNameResults + ".edf");

            if (ExperimentType != _ExperimentType.SmoothPursuit)
                SaveCSVFile(sDataPath + "\\" + FileNameResults);

            exp_status = ExperimentStatus.Ending;

            if (ExpInputMode == InputMode.P300Speller)
            {
                //Faccio riprendere la normale attività allo speller
                //UDPClientToConnIn.Send(dataPauseOff, dataPauseOff.Length);

                //lo spengo
                SendUDPString(sEnd);

                myExpW.ClearScreen();
            }
            else
            {
                CoordUpdateTimer.Stop();
                CoordUpdateTimer.Elapsed -= UpdateCoordTimerElapsedEvent;
                myLog.Log("Timer Stopped");
            }

            if (!bStandAlone)
                myTCW.Invoke(new EventHandler(delegate { myTCW.ExperimentCompleted(); }));

            if (bStandAlone)
            {
                //metto in pausa il P300Speller
                //UDPClientToConnIn.Send(dataPauseOn, dataPauseOn.Length);

                myExpW.Invoke(new EventHandler(delegate { myExpW.EndOfExperiment(); }));
            }

        }

        public void SetDataPath(string _newpath)
        {
            sDataPath = _newpath;
        }


        public void SetSubjectID(string _new_ID)
        {
            sSubjectID = _new_ID;
        }

        public void SaveCSVFile(string filename)
        {
            //            DateTime last_dt;
            TextWriter tw;
            int ReasTimeListCounter = 0;
            int TrialCounter = 0;
            try
            {
                //Salvo soltanto se la lista di risultati contiene qualche valore

                if (myResultList.Count > 0)
                {
                    //controllo se ho passato una stringa corretta oppure no
                    if (filename == "")
                        tw = new StreamWriter("results.csv");
                    else
                        tw = new StreamWriter(filename + ".csv");

                    if (ExpInputMode == InputMode.P300Speller)
                        tw.WriteLine("Time (ms); Reasoning Time (ms); Selection Time (ms); Choice; Value; BCI Choice; Note");
                    else
                        tw.WriteLine("Time (ms); Selection Time (ms); Choice; Value; Note");

                    //last_dt = myResultList[0].dt;//ExperimentStartTime;
                    int k = 0;
                    foreach (ResultListElement rle in myResultList)
                    {
                        TimeSpan AbsTrialTime = TrialStartTimeList[TrialCounter] - ExperimentStartTime;
                        TimeSpan sel_time = rle.dt - TrialStartTimeList[TrialCounter];

                        string AbsTime, SelTime, Val; //, sBCIChoice;
                        AbsTime = AbsTrialTime.TotalMilliseconds.ToString("000000");
                        SelTime = sel_time.TotalMilliseconds.ToString("000000");
                        Val = (Math.Round(rle.d_value, 0)).ToString("000");

                        //sBCIChoice = BCISelectionList[k++].ToString();
                        //se 
                        if (ExpInputMode == InputMode.P300Speller)
                        {
                            TimeSpan ReasonTime = SpaceKeyPressTimeList[ReasTimeListCounter++] - TrialStartTimeList[TrialCounter];
                            string reastime = ReasonTime.TotalMilliseconds.ToString("000000");

                            tw.WriteLine(AbsTime + "; " + reastime + ";" + SelTime + "; " + rle.s_value + ";" + Val + "; " + BCISelectionList[k++] + "; " + rle.s_note);
                        }
                        else
                            tw.WriteLine(AbsTime + "; " + SelTime + "; " + rle.s_value + ";" + Val + "; " + rle.s_note);

                        TrialCounter++;

                        //last_dt = rle.dt;
                    }

                    tw.Close();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Save CSV file error: " + ex.Message);
            }
        }

        public virtual void UpdateConstantROIList()
        {

            int x, y, x0, y0, w, h;
            string sx, sy;
            bool active;
            string item;
            int value;

            try
            {
                if (bTrainingMode)
                    ActROIs = TrainingTrials.Item(ActualTrialNumber).SelectNodes("ROIs/ROI");
                else
                    ActROIs = Trials.Item(ActualTrialNumber).SelectNodes("ROIs/ROI");

                //lock (RoiListLock)
                //{
                //RoiMutex.WaitOne();
                lock (myROIList)
                    myROIList.myRoiList.Clear();

                //RoiMutex.ReleaseMutex();

                for (int k = 0; k < ROIs.Count; k++)
                {
                    //Calcolo i dati geometrici


                    w = int.Parse(ROIs.Item(k).SelectSingleNode("w").InnerText.ToString());
                    h = int.Parse(ROIs.Item(k).SelectSingleNode("h").InnerText.ToString());

                    sx = ROIs.Item(k).SelectSingleNode("x").InnerText.ToString();
                    sy = ROIs.Item(k).SelectSingleNode("y").InnerText.ToString();

                    if (sx.Contains(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol))
                    {
                        x = int.Parse(sx.Replace(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol, ""));
                        x0 = (x * ScreenWidth) / 100 - w / 2;
                    }
                    else
                        x0 = int.Parse(sx) - w / 2;

                    if (sy.Contains(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol))
                    {
                        y = int.Parse(sy.Replace(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol, ""));
                        y0 = (y * ScreenHeight) / 100 - h / 2;
                    }
                    else
                        y0 = int.Parse(sy) - h / 2;


                    //x0 = (x * myCanvas.Width) / 100 - w / 2;
                    //y0 = (y * myCanvas.Height) / 100 - h / 2;


                    active = bool.Parse(ROIs.Item(k).SelectSingleNode("active").InnerText.ToString());
                    item = ROIs.Item(k).SelectSingleNode("item").InnerText.ToString();

                    if (ActROIs.Item(k).SelectSingleNode("value").InnerText.ToString() != "")
                        value = int.Parse(ActROIs.Item(k).SelectSingleNode("value").InnerText.ToString());
                    else
                        value = 0;
                    //ROIs.Item(k).SelectSingleNode("value").InnerText.ToString());

                    string sID = ROIs.Item(k).SelectSingleNode("ID").InnerText;
                    //RoiMutex.WaitOne();
                    lock (myROIList)
                        myROIList.AddElement(x0, y0, w, h, active, sID, item, value);
                    //RoiMutex.ReleaseMutex();
                }
                //}
            }
            catch (Exception ex)
            {
                MessageBox.Show("UpdateConstantROIList error: " + ex.Message + " - " + ex.StackTrace);
            }
        }

        public virtual void UpdateVariableROIList()
        {
            int x, y, x0, y0, w, h;
            string sx, sy;
            bool active;
            string item;
            int value;

            try
            {
                if (bTrainingMode)
                    ActROIs = TrainingTrials.Item(ActualTrialNumber).SelectNodes("ROIs/ROI");
                else
                    ActROIs = Trials.Item(ActualTrialNumber).SelectNodes("ROIs/ROI");

                lock (myROIList)
                    myROIList.myRoiList.Clear();


                for (int k = 0; k < ActROIs.Count; k++)
                {
                    //Calcolo i dati geometrici
                    w = int.Parse(ActROIs.Item(k).SelectSingleNode("w").InnerText.ToString());
                    h = int.Parse(ActROIs.Item(k).SelectSingleNode("h").InnerText.ToString());

                    sx = ActROIs.Item(k).SelectSingleNode("x").InnerText.ToString();
                    sy = ActROIs.Item(k).SelectSingleNode("y").InnerText.ToString();

                    x = int.Parse(sx.Replace(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol, ""));
                    y = int.Parse(sy.Replace(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol, ""));

                    //x0 = (x * myCanvas.Width) / 100 - w / 2;
                    //y0 = (y * myCanvas.Height) / 100 - h / 2;
                    x0 = (x * ScreenWidth) / 100 - w / 2;
                    y0 = (y * ScreenHeight) / 100 - h / 2;

                    active = bool.Parse(ActROIs.Item(k).SelectSingleNode("active").InnerText.ToString());
                    item = ActROIs.Item(k).SelectSingleNode("item").InnerText.ToString();
                    if (ActROIs.Item(k).SelectSingleNode("value").InnerText.ToString() != "")
                        value = int.Parse(ActROIs.Item(k).SelectSingleNode("value").InnerText.ToString());
                    else
                        value = 0;
                    //ROIs.Item(k).SelectSingleNode("value").InnerText.ToString());
                    string sID = ActROIs.Item(k).SelectSingleNode("ID").InnerText;

                    //RoiMutex.WaitOne();
                    lock (myROIList)
                        myROIList.AddElement(x0, y0, w, h, active,sID, item, value);
                    //RoiMutex.ReleaseMutex();
                }
                //}
            }
            catch (Exception ex)
            {
                MessageBox.Show("UpdateVariableROIList error: " + ex.Message + " - " + ex.StackTrace);
            }

        }

        #region UPDATE CANVAS

        public void ClearCanvas()
        {
            //lock (GraphicLock)
            //{
            try
            {
                //GraphMutex.WaitOne();
                myExpW.LockCanvasUserCopy();
                Graphics CanvasGraphics = Graphics.FromImage(myExpW.CanvasUserCopy);
                CanvasGraphics.Clear(bkgrnd);
                CanvasGraphics.Dispose();
                myExpW.CopyCanvas();
                myExpW.UnlockCanvasUserCopy();

                //GraphMutex.ReleaseMutex();
                //myExpW.Invalidate();
            }
            catch (Exception ex)
            {
                MessageBox.Show("ClearCanvas error:" + ex.Message + " - " + ex.StackTrace);
            }//}
        }


        #endregion

        #region Eyetracker


        //TODO: verificare se sia da spostare nella classe Eyetracking
        public void UpdateHostROI()
        {
            //Pulisco lo schermo sull'Host
            ET.SendCommand("clear_screen 7");

            //Disegnamo i quadrati delle ROIs
            for (int i = 0; i < myROIList.myRoiList.Count; i++)
            {
                int x0, y0, x1, y1, xt, yt;
                x0 = myROIList.myRoiList[i].x0;
                y0 = myROIList.myRoiList[i].y0;
                x1 = myROIList.myRoiList[i].x1;
                y1 = myROIList.myRoiList[i].y1;
                xt = x0 + (x1 - x0) / 2;
                yt = y0 + (y1 - y0) / 2;

                //disegno il contorno
                ET.SendCommand("draw_box " + x0.ToString() + " " + y0.ToString() + " " +
                    x1.ToString() + " " + y1.ToString() + "  0");

                //disegno la lettera
                ET.SendCommand("draw_text " + xt.ToString() + " " + yt.ToString() + " 0 " + myROIList.myRoiList[i].sItem);
            }
        }

        public virtual void ET_LabelTrial()
        {
            ET.SendCommand("record_status_message '" + "Trial ID=" + ActualTrialNumber.ToString() + "'");

            //[per DataViewer] Mando la sequenza dei nomi delle variabili prima di iniziare
            if (ExperimentType == _ExperimentType.Saccade)
                ET.SendMessage("TRIAL_VAR_LABELS SubjectID Type Delay");
            else
                ET.SendMessage("TRIAL_VAR_LABELS SubjectID");

            //[per DataViewer] Mando il TrialID prima di iniziare
            ET.SendMessage("TRIALID " + ActualTrialNumber.ToString());

            //ET.SendMessage("!V TRIAL_VAR " + ActualTrialNumber.ToString());
        }

        #endregion

        #region PAINTEVENT
        public void PaintCanvasOnTCW()
        {
            //Graphics g = myTCW.pictureBox1.CreateGraphics();

            try
            {
                myTCW.Invoke(new EventHandler(delegate
                {
                    //GraphMutex.WaitOne();
                    myExpW.CopyBackCanvas();
                    myExpW.LockCanvasUserCopy();
                    myTCW.pictureBox1.Image = myExpW.CanvasUserCopy;
                    myExpW.UnlockCanvasUserCopy();
                    myTCW.Invalidate();
                }));

                //GraphMutex.ReleaseMutex();
            }
            catch (Exception ex)
            {
                MessageBox.Show("PaintCanvasOnTCW error: " + ex.Message + " - " + ex.Source.ToString());

            }
            //g.DrawImage(myCanvas, 0,0);
            //myTCW.Invalidate();
            //g.Dispose();
        }


        public void PaintCountdown()
        {
            int x, y, w, h;

            x = ScreenWidth / 5;
            y = ScreenHeight / 5;
            w = (ScreenWidth * 6) / 10;
            h = (ScreenHeight * 6) / 10;

            StringFormat drawFormat = new StringFormat();
            drawFormat.Alignment = StringAlignment.Center;

            // Create font and brush.

            //lock (GraphicLock)
            //{
            //GraphMutex.WaitOne();
            myExpW.LockCanvasUserCopy();
            Graphics g = Graphics.FromImage(myExpW.CanvasUserCopy);
            g.Clear(bkgrnd);
            g.DrawString(sCountDown, new Font("Arial", 300), new SolidBrush(frgrnd), new Rectangle(x, y, w, h), drawFormat);
            g.Dispose();
            myExpW.CopyCanvas();
            myExpW.UnlockCanvasUserCopy();
            //GraphMutex.ReleaseMutex();
            //}
        }

        #endregion

        public void FromTrainingToTrials()
        {
            bTrainingMode = false;

            StopPeriodicTimer();

            if (ExpInputMode == InputMode.Eyetracker)
            {
                ET.StopRecording(false, "");

                //Thread.Sleep(200);
                //ET.StartRecording();
            }

            if (bShowTrainingScore)
            {
                ShowTrainingScore();
                myExpW.Invoke(new EventHandler(delegate { myExpW.Refresh(); }));
            }
            else
            {
                //if (bInitialInstructions)
                ShowInitialInstructions();
                myExpW.Invoke(new EventHandler(delegate { myExpW.Refresh(); }));
                //Thread.Sleep(100);
            }
            if (!bStandAlone)
                PaintCanvasOnTCW();

            if (!bStandAlone)
                myTCW.Invoke(new EventHandler(delegate { myTCW.PromptToStartTrials(bTrainingAvailable); }));
            //bTrainingMode = myTCW.PromptToStartTrainingOrTrials();

            ActualTrialNumber = 0;

            //StartTrials();

        }


        public void ShowTrainingScore()
        {
            try
            {
                int x, y, w, h;
                string s;

                exp_status = ExperimentStatus.DisplayTrainingResults;

                myExpW.LockCanvasUserCopy();
                myExpW.HighlightEnabled = false;
                Graphics g = Graphics.FromImage(myExpW.CanvasUserCopy);

                x = ScreenWidth / 10;
                y = ScreenHeight / 3;
                w = (ScreenWidth * 8) / 10;
                h = (ScreenHeight * 8) / 10;

                // Create font and brush.
                Font drawFont;
                if (InstructionsFontSize > 0)
                    drawFont = new Font("Arial", InstructionsFontSize);
                else
                    drawFont = new Font("Arial", 22);

                SolidBrush drawBrush = new SolidBrush(frgrnd);

                g.Clear(bkgrnd);
                //g.DrawRectangle(new Pen(frgrnd), x, y, w, h);

                // Set format of string.
                StringFormat mydrawFormat = new StringFormat();
                mydrawFormat.Alignment = StringAlignment.Center;

                s = CorrectScoreString + " = " + TrialScore.ToString() + Environment.NewLine;
                s += ErrorScoreString + " = " + (ActualTrialNumber - TrialScore).ToString() + Environment.NewLine;
                s += TimeoutScoreString + " = " + NumOfTimeout.ToString();

                g.DrawString(s, drawFont, drawBrush, new Rectangle(x, y, w, h), mydrawFormat);

                g.Dispose();

                //GraphMutex.ReleaseMutex();

                myExpW.CopyCanvas();

                myExpW.UnlockCanvasUserCopy();

            }
            catch (Exception ex)
            {
                myLog.Log("ShowTrainingScore error : " + ex.Message + " " + ex.Source);
                MessageBox.Show("ShowTrainingScore error : " + ex.Message + " " + ex.Source);
            }

        }


        #region TIMER

        //**********************************************************************************
        //                               TIMER 
        //**********************************************************************************


        public void CountDown(int NumberToCount, int period_ms)
        {
            sCountDown = NumberToCount.ToString();

            exp_status = ExperimentStatus.CountDown;

            //myExpW.Invalidate();

            int period_cut_ms = period_ms / 10;

            for (int k = NumberToCount; k > 0; k--)
            {
                sCountDown = k.ToString();
                PaintCountdown();
                //UpdateGraphics();
                myExpW.Invoke(new EventHandler(delegate { myExpW.Refresh(); }));

                if (bUseSounds)
                    Beep(1000, 300);

                for (int i = 0; i < 10; i++)
                {
                    Thread.Sleep(period_cut_ms);
                    Application.DoEvents();
                }
            }
        }

        #endregion



        #region UTILITIES


        public void AwakeWait(int num_ms)
        {
            if (num_ms < 100)
                Thread.Sleep(num_ms);
            else
                if (num_ms < 1000)
                {
                    for (int i = 0; i < num_ms; i += 50)
                    {
                        Thread.Sleep(50);
                        Application.DoEvents();
                    }
                }
                else
                {
                    for (int i = 0; i < num_ms; i += 100)
                    {
                        Thread.Sleep(100);
                        Application.DoEvents();
                    }
                }
        }

        //public void WaitForAFlag(ref bool flag)
        //{
        //    while (!flag)
        //    {
        //        AwakeWait(100);
        //    }
        //}

        #endregion
    }

}
